use crate::{client::RedisClient, resp::Command, RedisResult};

use super::{InsertMode, ListPosition};

impl RedisClient {
    pub async fn list_lpush(&self, key: &str, value: impl Into<String>) -> RedisResult<usize> {
        let cmd = Command::new_with_key("LPUSH", key).arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn list_lpush_more(&self, key: &str, values: &[&str]) -> RedisResult<usize> {
        let mut cmd = Command::new_with_key("LPUSH", key);
        for item in values.iter() {
            cmd.arg_append(*item);
        }
        self.send_command(cmd).await
    }

    pub async fn list_lpushx(&self, key: &str, value: impl Into<String>) -> RedisResult<usize> {
        let cmd = Command::new_with_key("LPUSHX", key).arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn list_lpushx_more(&self, key: &str, values: &[&str]) -> RedisResult<usize> {
        let mut cmd = Command::new_with_key("LPUSHX", key);
        for item in values.iter() {
            cmd.arg_append(*item);
        }
        self.send_command(cmd).await
    }

    pub async fn list_rpush(&self, key: &str, value: impl Into<String>) -> RedisResult<usize> {
        let cmd = Command::new_with_key("RPUSH", key).arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn list_rpush_more(&self, key: &str, values: &[&str]) -> RedisResult<usize> {
        let mut cmd = Command::new_with_key("RPUSH", key);
        for item in values.iter() {
            cmd.arg_append(*item);
        }
        self.send_command(cmd).await
    }

    pub async fn list_rpushx(&self, key: &str, value: impl Into<String>) -> RedisResult<usize> {
        let cmd = Command::new_with_key("RPUSHX", key).arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn list_rpushx_more(&self, key: &str, values: &[&str]) -> RedisResult<usize> {
        let mut cmd = Command::new_with_key("RPUSHX", key);
        for item in values.iter() {
            cmd.arg_append(*item);
        }
        self.send_command(cmd).await
    }

    pub async fn list_lindex(&self, key: &str, index: isize) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("LINDEX", key).arg(index);
        self.send_command(cmd).await
    }

    pub async fn list_linsert(
        &self,
        key: &str,
        insert_mode: InsertMode,
        pivot: impl Into<String>,
        value: impl Into<String>,
    ) -> RedisResult<isize> {
        let cmd = Command::new_with_key("LINSERT", key)
            .arg(insert_mode)
            .arg(pivot.into())
            .arg(value.into());
        self.send_command(cmd).await
    }

    pub async fn list_llen(&self, key: &str) -> RedisResult<isize> {
        let cmd = Command::new_with_key("LLEN", key);
        self.send_command(cmd).await
    }

    pub async fn list_lmove(
        &self,
        source: &str,
        destination: &str,
        source_pos: ListPosition,
        destination_pos: ListPosition,
    ) -> RedisResult<Option<String>> {
        let cmd = Command::new_with_key("LMOVE", source)
            .arg(destination)
            .arg(source_pos)
            .arg(destination_pos);
        self.send_command(cmd).await
    }

    pub async fn list_lmpop(
        &self,
        keys: &[&str],
        pos: ListPosition,
        count: Option<usize>,
    ) -> RedisResult<Option<Vec<String>>> {
        if keys.len() == 0 {
            return Err(crate::RedisError::Send(
                "the len of keys couldn't be 0".to_string(),
            ));
        }
        let mut cmd = Command::new("LMPOP").arg(keys.len());
        for key in keys {
            cmd.arg_append(*key);
        }
        cmd.arg_append(pos);
        if let Some(count) = count {
            cmd.arg_append("COUNT").arg_append(count);
        }
        self.send_command(cmd).await
    }

    pub async fn list_lpop(
        &self,
        key: &str,
        count: Option<usize>,
    ) -> RedisResult<Option<Vec<String>>> {
        let mut cmd = Command::new_with_key("LPOP", key);
        if let Some(count) = count {
            cmd.arg_append(count);
        }
        self.send_command(cmd).await
    }

    /// `key` the key of list to blpop
    ///
    /// `timeout` this value should less than the [`redius::ConnectionState.receive_timeout`]
    pub async fn list_blpop(&self, key: &str, timeout: u16) -> RedisResult<Option<Vec<String>>> {
        let cmd = Command::new_with_key("BLPOP", key).arg(timeout);
        self.send_command(cmd).await
    }
}
